// TODO: 书写diff算法子节点更新策略
import patchVnode from './patchVnode'
import createElement from './createElement'

export default function updateChildren (parentElm, oldCh, newCh) {
    // 创建四个指针
    let oldStartIdx = 0                 // 旧前
    let newStartIdx = 0                 // 新前
    let oldEndIdx = oldCh.length - 1    // 旧后
    let newEndIdx = newCh.length - 1    // 新后
    // 创建四个指针对应节点
    let oldStartVnode = oldCh[oldStartIdx]      // 旧前节点
    let oldEndVnode = oldCh[oldEndIdx]          // 旧后节点
    let newStartVnode = newCh[newStartIdx]      // 新前节点
    let newEndVnode = newCh[newEndIdx]          // 新后节点
    // 设置一个 keyMap 映射表
    let keyMap = null
    // 判断
    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
        // 0 判断是否是标记节点，标记节点跳过判断
        if (oldStartIdx === 'tag') {
            oldStartVnode = oldCh[++oldStartIdx]
        }
        else if (oldEndIdx === 'tag') {
            oldEndVnode = oldCh[--oldEndIdx]
        }
        else if (newStartIdx === 'tag') {
            newStartVnode = newCh[++newStartIdx]
        }
        else if (newEndIdx === 'tag') {
            newEndVnode = newCh[--newEndIdx]
        }
        // 1 新前与旧前
        else if (checkSameVnode(newStartVnode, oldStartVnode)) {
            console.log('新前与旧前');
            // 分析新旧节点，进行替换
            patchVnode(oldStartVnode, newStartVnode)
            newStartVnode = newCh[++newStartIdx]
            oldStartVnode = oldCh[++oldStartIdx]
        }
        // 2 新后与旧后
        else if (checkSameVnode(newEndVnode, oldEndVnode)) {
            console.log('新后与旧后');
            patchVnode(oldEndVnode, newEndVnode)
            newEndVnode = newCh[--newEndIdx]
            oldEndVnode = oldCh[--oldEndIdx]
        }
        // 3 新后与旧前
        else if (checkSameVnode(newEndVnode, oldStartVnode)) {
            console.log('新后与旧前');
            patchVnode(oldStartVnode, newEndVnode)
            // 命中后，此时要移动节点：旧前指向节点 移动到 旧后指向节点 的后面
            parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling)
            newEndVnode = newCh[--newEndIdx]
            oldStartVnode = oldCh[++oldStartIdx]
        }
        // 4 新前与旧后
        else if (checkSameVnode(newStartVnode, oldEndVnode)) {
            console.log('新前与旧后');
            patchVnode(oldEndVnode, newStartVnode)
            // 命中后，此时要移动节点：旧后指向节点 移动到 旧前指向节点 的前面
            parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm)
            newStartVnode = newCh[++newStartIdx]
            oldEndVnode = oldCh[--oldEndIdx]
        }
        // 5 未命中四种情况
        else {
            // 1 设置 keyMap 映射表
            if (!keyMap) {
                keyMap = {}
                for (let i = oldStartIdx; i <= oldEndIdx; i++) {
                    let key = oldCh[i].data.key
                    keyMap[key] = i
                }
            }
            // 2 获取 新前的key值 在映射表中的索引位置
            let newInOldIdx = keyMap[newStartVnode.data.key]
            // 3 判断
            if (newInOldIdx === undefined) {
                // 不存在，是全新的节点
                parentElm.insertBefore(createElement(newStartVnode), oldStartVnode.elm)
            }
            else {
                // 旧节点中存在，进行移动
                let moveVnode = oldCh[newInOldIdx]
                // 移动节点
                if (moveVnode) {
                    patchVnode(moveVnode, newStartVnode)
                    oldCh[newInOldIdx] = 'tag'
                    parentElm.insertBefore(moveVnode.elm, oldStartVnode.elm)
                }
            }
            newStartVnode = newCh[++newStartIdx]
        }
    }
    // 查看 新节点 是否还有剩余，有则追加
    if (newStartIdx <= newEndIdx) {
        // 1 插入的标杆
        let before = newCh[newEndIdx + 1] ? newCh[newEndIdx + 1].elm : null
        for (let i = newStartIdx; i <= newEndIdx; i++) {
            // 2 插入到页面中
            parentElm.insertBefore(createElement(newCh[i]), before)
        }
    }
    // 查看 旧节点 是否还有剩余，有则删除
    else if (oldStartIdx <= oldEndIdx) {
        for (let i = oldStartIdx; i <= oldEndIdx; i++) {
            // 删除子节点
            if (oldCh[i] === 'tag') continue;
            parentElm.removeChild(oldCh[i].elm)
        }
    }
}

// TODO: 判断两个虚拟节点是否相同
function checkSameVnode (vnode1, vnode2) {
    return vnode1.sel === vnode2.sel && vnode1.data.key === vnode2.data.key
}